package example.server;

import java.io.*;
import java.net.*;
import java.nio.*;
import java.nio.channels.*;
import java.nio.charset.*;
import java.util.*; // For Set and Iterator

public class NonBlockingServer {
	public static void main(String[] args) throws IOException {

		// Get the character encoders and decoders you'll need
		Charset charset = Charset.forName("ISO-8859-1");
		CharsetEncoder encoder = charset.newEncoder();
		CharsetDecoder decoder = charset.newDecoder();

		// Allocate a buffer for communicating with clients
		ByteBuffer buffer = ByteBuffer.allocate(512);

		// All of the channels in this code will be in nonblocking mode.
		// So create a Selector object that will block while monitoring
		// all of the channels and stop blocking only when one or more
		// of the channels is ready for I/O of some sort.
		Selector selector = Selector.open();

		// Create a new ServerSocketChannel and bind it to port 8000
		// Note that this must be done using the underlying ServerSocket
		ServerSocketChannel server = ServerSocketChannel.open();
		server.socket().bind(new java.net.InetSocketAddress(888));
		// Put the ServerSocketChannel into nonblocking mode
		server.configureBlocking(false);
		// Now register it with the Selector (note that register() is called
		// on the channel, not on the selector object, however).
		// The SelectionKey represents the registration of this channel with
		// this Selector.
		SelectionKey serverkey = server.register(selector,
				SelectionKey.OP_ACCEPT);

		for (;;) { // The main server loop. The server runs forever.
			// This call blocks until there is activity on one of the
			// registered channels. This is the key method in nonblocking
			// I/O.
			System.out.println("Waiting I/O EVENT..");
			selector.select();
			System.out.println("Detect I/O EVENT..");
			
			// Get a java.util.Set containing the SelectionKey objects for
			// all channels that are ready for I/O.
			Set keys = selector.selectedKeys();

			// Use a java.util.Iterator to loop through the selected keys
			for (Iterator i = keys.iterator(); i.hasNext();) {
				// Get the next SelectionKey in the set and remove it
				// from the set. It must be removed explicitly, or it will
				// be returned again by the next call to select().
				
				SelectionKey key = (SelectionKey) i.next();
				i.remove();

				// Check whether this key is the SelectionKey obtained when
				// you registered the ServerSocketChannel.
				if (key == serverkey) {
					// Activity on the ServerSocketChannel means a client
					// is trying to connect to the server.
					if (key.isAcceptable()) {
						
						// Accept the client connection and obtain a
						// SocketChannel to communicate with the client.
						SocketChannel client = server.accept();
						// Put the client channel in nonblocking mode
						client.configureBlocking(false);
						// Now register it with the Selector object,
						// telling it that you'd like to know when
						// there is data to be read from this channel.
						SelectionKey clientkey = client.register(selector,
								SelectionKey.OP_READ);
						// Attach some client state to the key. You'll
						// use this state when you talk to the client.
						clientkey.attach(new Integer(0));
						
						System.out.println("Client Accepted..");
					}
				} else {
					// If the key obtained from the Set of keys is not the
					// ServerSocketChannel key, then it must be a key
					// representing one of the client connections.
					// Get the channel from the key.
					SocketChannel client = (SocketChannel) key.channel();

					// If you are here, there should be data to read from
					// the channel, but double-check.
					if (!key.isReadable())
						continue;

					// Now read bytes from the client. Assume that all the
					// client's bytes are in one read operation.
					int bytesread = client.read(buffer);

					// If read() returns -1, it indicates end-of-stream,
					// which means the client has disconnected, so
					// deregister the selection key and close the channel.
					if (bytesread == -1) {
						key.cancel();
						client.close();
						continue;
					}

					// Otherwise, decode the bytes to a request string
					buffer.flip();
					String request = decoder.decode(buffer).toString();
					buffer.clear();
					// Now reply to the client based on the request string
					System.out.println("String from Client >" + request);
					if (request.trim().equals("quit")) {
						// If the request was "quit", send a final message
						// Close the channel and deregister the
						// SelectionKey
						client.write(encoder.encode(CharBuffer.wrap("Bye.")));
						key.cancel();
						client.close();
					} else {
						// Otherwise, send a response string comprised of
						// the sequence number of this request plus an
						// uppercase version of the request string. Note
						// that you keep track of the sequence number by
						// "attaching" an Integer object to the
						// SelectionKey and incrementing it each time.

						// Get sequence number from SelectionKey
						int num = ((Integer) key.attachment()).intValue();
						// For response string
						String response = num + ": " + request.toUpperCase();
						// Wrap, encode, and write the response string
						client.write(encoder.encode(CharBuffer.wrap(response)));
						// Attach an incremented sequence nubmer to the key
						key.attach(new Integer(num + 1));
						
						System.out.print("Send>" + response);
					}
				}
			}
		}
	}
	
	private static void sendFile(){
		
		return ;
	}
	
}